require 'uuid'
require 'mp3info'

class AudioAsset < ActiveRecord::Base
  include RadiaStore::StorageSystem

  has_many                :occurrences
  has_many                :replicas
  has_and_belongs_to_many :broadcasts
  
  acts_as_authorizable
  acts_as_taggable
  
  validates_presence_of   :name

  before_save             :set_filename, :set_storage_name
  after_save              :store_files
  # after_destroy :destroy_files
  
  EssenceFileType  = 'essence'
  MetadataFileType = 'metadata'
  
  FORMATS = %w(mp3 ogg wma wav mp4).sort
  GENRES = Mp3Info::GENRES.sort
  
  ####################################
  # Specialized Getters to attributes
  ####################################
  
  def programs
    Program.find_by_audio_asset(self)
  end
  
  def authors
    self.has_asset_authors
  end
  
  def available?
    return false unless self.essence_file?
    self.find_main_essence.available?
  end
  
  def length_in_megabytes
    (self.length.to_f / 1024 / 1024).round(2)
  end
  
  def essence_file?
    self.find_main_essence ? true : false
  end
  
  def metadata_file?
    self.find_main_metadata ? true : false
  end
  ####################################
  
  
  ####################################
  # Setters to files and specialized setters
  ####################################
  def set_filename
    self.filename = sanitize_filename(self.name)
  end
  
  def set_storage_name
    self.storage_name = UUID.new :compact
  end

  # Setter to handle file associated to audio asset
  def file=(essence)
    unless essence.blank? or essence.size.zero?
  	  # Test if the file size is greater than MAX_FILE_SIZE (config/initializers/storage.rb) megabytes, and validates de content_type
  	  if essence.size <= MAX_FILE_SIZE && !validate_content_type(essence).nil?
        @essence = essence
    		process_content_type
  	    @buffer_essence = essence.read
  	    write_attribute("length", essence.size.to_s)
  		  write_attribute("md5_hash", Digest::MD5.hexdigest(@buffer_essence))
  	  end
  	end
  end
  
  # Setter to handle metadata file associated to audio asset
  def metadata_file=(metadata)
    unless metadata.blank? or metadata.size.zero?
  	  @metadata_essence = metadata.read if metadata.size <= MAX_FILE_SIZE && !validate_content_type(metadata).nil?
    end
  end
  
  def location=(uri)
    unless uri.blank?
    end
  end
  #################################

  # Add tags to audio asset.
  def add_tags(tags)
    self.tag_list.add(tags)
  end
  
  def tag_cloud
    self.tag_list.blank? ? [] : self.tag_counts
  end
  
  # Class method for retrieve tag_cloud
  def self.tag_cloud
    self.tag_counts.blank? ? [] : self.tag_counts
  end
  
  def self.top_tagged(limit = 10)
    total = AudioAsset.tag_cloud.size
    return nil if total.zero?
    
    limit = (limit > total) ? total : limit
    
    AudioAsset.tag_cloud.sort_by(&:count).reverse[0..(limit - 1)]
  end


  # Mark an audio asset to replicate, including metadata
  def replicate(replication_system)
    # Tests if audio_asset is already replicated in replication_system
    return if replication_system.has_asset_replica?(self)
    
    # Mark asset
    replica = Replica.new_to_replicate
    replica.replica_system = replication_system
    replica.audio_asset = self
    if replica.save
      # Create fetcher
      fetcher = Fetcher.new
      fetcher.action = Fetcher::Replicate
      fetcher.done = false
      fetcher.replica = replica
      
      # Save fetch, check if asset has a metadata file associated and replicate it
      if fetcher.save && self.metadata_file?
        # Mark metadata
        metadata_replica = Replica.new_to_replicate_metadata
        metadata_replica.replica_system = replication_system
        metadata_replica.audio_asset = self
        if metadata_replica.save
          metadata_fetcher = Fetcher.new
          metadata_fetcher.action = Fetcher::Replicate
          metadata_fetcher.done = false
          metadata_fetcher.replica = metadata_replica
          metadata_fetcher.save
        end
      end
    end
  end
  
  # Removes audio_assets replicas, including metadata
  def remove_replication(replication_system)
    replicas = Replica.find :all, :conditions => { :audio_asset_id => self.id, :replica_system_id => replication_system.id }
    
    # Ensure that if there are more than one replica (e.g. essence and metadata) of this audio_asset, are all treated
    replicas.each do |replica|
      # Make replica unavailable
      if replica.available?
        replica.available = false
        replica.save
      end
      
      # If some action to replicate this resource is set, remove it if :done is false
      fetchers = Fetcher.find :all, :conditions => { :replica_id => replica.id, :action => Fetcher::Replicate }
      unless fetchers.nil?
        fetchers.each do |fetcher|
          if fetcher.done
            # Create a new fetcher to remove resource from replication system
            new_fetcher = Fetcher.new
            new_fetcher.action = Fetcher::Delete
            new_fetcher.done = false
            new_fetcher.replica = replica
            new_fetcher.save
          else
            # Destroy resource's replica and respective fetchers
            replica.destroy
          end
        end
      end
    end
  end
  
  #################################
  # Specialized Finders and Search
  #################################
  def find_where_asset_is_replicated
    replicas = ReplicaSystem.find_remote_replication_systems
    active_replicas = []
    
    replicas.each do |replica|
      active_replicas << replica if replica.has_asset_replica? self
    end
    
    active_replicas
  end
  
  def find_where_asset_is_not_replicated
    replicas = ReplicaSystem.find_remote_replication_systems
    inactive_replicas = []
    
    replicas.each do |replica|
      inactive_replicas << replica unless replica.has_asset_replica? self
    end
    
    inactive_replicas
  end
  
  def find_main_essence
    self.replicas.find :first, :conditions => { :main => true, :available => true, :file_type => AudioAsset::EssenceFileType }
  end
  
  def find_main_metadata
    self.replicas.find :first, :conditions => { :main => true, :available => true, :file_type => AudioAsset::MetadataFileType }
  end

  def self.find_by_tags(tags)
    self.find_tagged_with tags
  end
  
  def self.find_recent(limit = 3)
    self.find :all, :order => 'created_at desc', :limit => limit
  end
  
  def self.find_by_replica_system(replica_system)
    AudioAsset.find_by_sql "SELECT a.* FROM replicas as r, audio_assets as a 
      WHERE r.replica_system_id = #{replica_system.id} AND r.audio_asset_id = a.id AND r.file_type = '#{AudioAsset::EssenceFileType}' AND r.id NOT IN (
        SELECT r1.id FROM replicas as r1, fetchers as f WHERE r1.id = f.replica_id AND f.action = '#{Fetcher::Delete}')"
  end
  
  
  def self.search(keys)
    conditions = []    
    search_columns = %w(name description format genre)
    search_columns.each { |element| conditions << (element + " LIKE '%#{keys}%'") }

    AudioAsset.find :all, :conditions => conditions.join(' or '), :order => :name
  end
  #################################
  
  
  def download
    sys = DownloadSystem.new
    sys.get_essence_file(self)
    # "#{MAIN_REPOSITORY_PATH}" + self.find_main_essence.replica_asset_uri
  end
  
  def download_metadata
    sys = DownloadSystem.new
    sys.get_metadata_file(self)
  end
  
  ####################################
  # Access Control Methods
  ####################################
  
  def can_be_replicated_by(user)
    return false if user.blank?
    user.is_administrator?
  end
  
  def is_updatable_by(user)
    return false if user.blank?
    true
  end
  
  def is_deletable_by(user)
    return false if user.blank?
	
    false
  end
  
  def self.is_readable_by(user, object = nil)
    return false if user.blank?
	
    false
  end
  
  def self.is_creatable_by(user)
    return false if user.blank?
    user.is_author?
  end
  
  private
  # Returns the extension of the filename (with dot). E.g.: .mp3
  def file_extension
    "." + @essence.original_filename.split(".").last
  end
  
  # Returns filename camelized. Replace non-alphanumeric, underscore and periods with "camelization"
  def sanitize_filename(value)
    # get only the filename, not the whole path
    just_filename = value.gsub(/^.*(\\|\/)/, '')
    # NOTE: File.basename doesn't work right with Windows paths on Unix
    # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) 

    # Finally, replace all non alphanumeric, underscore or periods with underscore in the filename and camilizes it
    just_filename.gsub(/[^\w\.\-]/,'_').camelize
  end

  # Accepts audio/*, text/*, application/ogg
  # Returns nil if the content_type doesn't match any of the listed above
  def validate_content_type(file)
    file.content_type =~ /(audio\/.*)|(text\/.*)|(application\/ogg)/
  end
  
  # Method that handles the storage of files associated to audio asset
  def store_files
      if @essence
    	  storage = UploadSystem.new
        
        begin
    	    # Store file
    	    replica_asset_uri, replica_system_id = storage.store_file(self.storage_name, file_extension, @buffer_essence)
      		# Create main replica
      		Replica.new do |replica|
      		  replica.audio_asset_id = self.id
      		  replica.replica_system_id = replica_system_id
      		  replica.replica_asset_uri = replica_asset_uri
      		  replica.file_type = AudioAsset::EssenceFileType
      		  replica.main = true
            replica.available = true
      		  replica.save
      		end
    
      		if @metadata_essence
      		  begin
      		    # Store metadata file
      		    replica_asset_uri, replica_system_id = storage.store_file(self.storage_name, '.xml', @metadata_essence)
        			# Create main replica
        			Replica.new do |replica|
      		      replica.audio_asset_id = self.id
      		      replica.replica_system_id = replica_system_id
      		      replica.replica_asset_uri = replica_asset_uri
      		      replica.file_type = AudioAsset::MetadataFileType
      		      replica.main = true
                replica.available = true
      		      replica.save
      		    end
      		  rescue
      		    # Do nothing
      		  end
      		end
    	
      rescue FileNotStoredException => message
    	  logger.info("FileNotStoredException: Error on saving " + file_name + " file. Msg: " + message)
      end
  	end
  end
  
  def process_content_type
    # It's a MP3 file. Get all relevant information from file
    if @essence.content_type == 'audio/mpeg'
	  #begin
	    #Mp3Info.open(@essence) do |mp3info|
		#  write_attribute("duration", mp3info.length)
	  #  write_attribute("bitrate", mp3info.bitrate)
	  #  write_attribute("samplerate", mp3info.samplerate)
	  #  write_attribute("channel_mode", mp3info.channel_mode)
	  #end
	  #rescue Exception => msg
	  #  logger.info("ERROR ON PROCESSING CONTENT_TYPE. MSG: " + msg)
	  #end
	end
  end
end
